package net.goutalk.glcs.module.form.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import net.goutalk.glcs.common.annotation.GlcsLog;
import net.goutalk.glcs.common.constant.GlobalConstant;
import net.goutalk.glcs.common.exception.MyException;
import net.goutalk.glcs.common.handler.FormContentStyleStrategy;
import net.goutalk.glcs.common.model.result.R;
import net.goutalk.glcs.common.utils.DatasourceUtil;
import net.goutalk.glcs.common.utils.LocalDateTimeUtil;
import net.goutalk.glcs.module.authority.dto.BatchSetDataAuthDto;
import net.goutalk.glcs.module.form.dto.*;
import net.goutalk.glcs.module.form.entity.FormDesignConfig;
import net.goutalk.glcs.module.form.entity.FormRelease;
import net.goutalk.glcs.module.form.entity.FormReleaseConfig;
import net.goutalk.glcs.module.form.entity.FormTemplate;
import net.goutalk.glcs.module.form.service.IFormExecuteService;
import net.goutalk.glcs.module.form.service.IFormReleaseService;
import net.goutalk.glcs.module.form.service.IFormTemplateService;
import net.goutalk.glcs.module.generator.constant.ComponentTypeConstant;
import net.goutalk.glcs.module.generator.entity.ColumnConfig;
import net.goutalk.glcs.module.generator.entity.TableConfig;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import oracle.sql.INTERVALDS;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;

/**
 * <p>
 * 自定义表单 执行控制器
 * </p>
 *
 * @author tanyujie
 * @since 2022-05-09
 */
@RestController
@RequestMapping(GlobalConstant.FORM_MODULE_PREFIX + "/execute")
@Api(value = GlobalConstant.FORM_MODULE_PREFIX + "/execute", tags = "自定义表单统一执行接口")
@AllArgsConstructor
public class FormExecuteController {

    private final IFormExecuteService formExecuteService;
    
    private final IFormReleaseService formReleaseService;
    
    private final IFormTemplateService formTemplateService;

    /**
     * 自定义表单 根据配置获取列表数据 不分页
     *
     * @param dto
     */
    @PostMapping(value = "/list")
    @ApiOperation(value = "自定义表单根据配置获取列表数据 不分页")
    @GlcsLog(value = "自定义表单根据配置获取列表数据 不分页")
    public R list(@Valid  @RequestBody FormExecuteListDto dto) {
        return R.ok(formExecuteService.list(dto));
    }


    /**
     * 自定义表单 根据配置获取列表数据 不分页
     *
     * @param dto
     */
    @PostMapping(value = "/app/list")
    @ApiOperation(value = "App 自定义表单根据配置获取列表数据 不分页")
    @GlcsLog(value = "App 自定义表单根据配置获取列表数据 不分页")
    public R appList(@Valid  @RequestBody AppFormExecuteListDto dto) {
        return R.ok(formExecuteService.appList(dto));
    }


    /**
     * 自定义表单 根据配置获取列表数据 分页
     *
     * @param dto
     */
    @PostMapping(value = "/page")
    @ApiOperation(value = "自定义表单根据配置获取列表数据 分页")
    @GlcsLog(value = "自定义表单根据配置获取列表数据 分页")
    public R page(@Valid  @RequestBody FormExecutePageDto dto) {
        return R.ok(formExecuteService.page(dto));
    }

    /**
     * App 自定义表单 根据配置获取列表数据 分页
     *
     * @param dto
     */
    @PostMapping(value = "/app/page")
    @ApiOperation(value = "App 自定义表单根据配置获取列表数据 分页")
    @GlcsLog(value = "App 自定义表单根据配置获取列表数据 分页")
    public R appPage(@Valid  @RequestBody AppFormExecutePageDto dto) {
        return R.ok(formExecuteService.appPage(dto));
    }


    /**
     * 自定义表单 根据配置获取 表单数据
     *
     * @param dto 发布表id
     */
    @PostMapping(value = "/info")
    @ApiOperation(value = "表单数据")
    @GlcsLog(value = "表单数据")
    public R info(@Valid @RequestBody FormExecuteInfoDto dto) {
        return R.ok(formExecuteService.info(dto));
    }

    /**
     * app 自定义表单 根据配置获取 表单数据
     *
     * @param dto
     */
    @PostMapping(value = "/app/info")
    @ApiOperation(value = "app 表单数据")
    @GlcsLog(value = "app 表单数据")
    public R appInfo(@Valid @RequestBody AppFormExecuteInfoDto dto) {
        return R.ok(formExecuteService.appInfo(dto));
    }

    /**
     * 自定义表单 根据配置获取 新增 表单数据
     *
     */
    @PostMapping(value = "/add")
    @ApiOperation(value = "新增")
    @GlcsLog(value = "新增表单数据")
    public R add(@Valid @RequestBody FormExecuteAddOrUpdateDto dto) {
        return R.ok(formExecuteService.add(dto));
    }

    /**
     * app 自定义表单 根据配置获取 新增 表单数据
     *
     */
    @PostMapping(value = "/app/add")
    @ApiOperation(value = "app 新增")
    @GlcsLog(value = "app 新增表单数据")
    public R appAdd(@Valid @RequestBody AppFormExecuteAddOrUpdateDto dto) {
        return R.ok(formExecuteService.appAdd(dto));
    }


    /**
     * 自定义表单 根据配置获取 修改 表单数据
     *
     */
    @PostMapping(value = "/update")
    @ApiOperation(value = "修改")
    @GlcsLog(value = "修改表单数据")
    public R update(@Valid @RequestBody FormExecuteAddOrUpdateDto dto) {
        return R.ok(formExecuteService.update(dto));
    }

    /**
     * app 自定义表单 根据配置获取 修改 表单数据
     *
     */
    @PostMapping(value = "/app/update")
    @ApiOperation(value = "app 修改")
    @GlcsLog(value = "app 修改表单数据")
    public R appUpdate(@Valid @RequestBody AppFormExecuteAddOrUpdateDto dto) {
        return R.ok(formExecuteService.appUpdate(dto));
    }

    /**
     * 自定义表单 根据配置获取 删除 表单数据
     *
     */
    @PostMapping(value = "/delete")
    @ApiOperation(value = "删除")
    @GlcsLog(value = "删除表单数据")
    public R delete(@Valid @RequestBody FormExecuteDeleteDto dto) {
        return R.ok(formExecuteService.delete(dto));
    }



    /**
     * app 自定义表单 根据配置获取 删除 表单数据
     *
     */
    @PostMapping(value = "/app/delete")
    @ApiOperation(value = "app 删除")
    @GlcsLog(value = "app 删除表单数据")
    public R appDelete(@Valid @RequestBody AppFormExecuteDeleteDto dto) {
        return R.ok(formExecuteService.appDelete(dto));
    }


    /**
     * 工作流模块 使用 自定义表单 根据配置获取 表单数据
     *
     * @param dto
     */
    @PostMapping(value = "/workflow/info")
    @ApiOperation(value = "表单数据")
    @GlcsLog(value = "表单数据")
    public R workFlowInfo(@Valid @RequestBody FormExecuteWorkflowInfoDto dto) {
        return R.ok(formExecuteService.workFlowInfo(dto));
    }


    @PostMapping(value = "/export")
    @ApiOperation(value = "导出")
    @GlcsLog(value = "导出表单数据")
    public ResponseEntity<byte[]> export(@Valid @RequestBody FormExecuteListDto dto) {
        FormRelease formRelease = formReleaseService.getById(dto.getReleaseId());
        FormReleaseConfig formReleaseConfig = JSONUtil.toBean(formRelease.getConfigJson(), FormReleaseConfig.class);
        // 配置excel第一行字段名
        List<List<String>> head = new ArrayList<>();
        List<ColumnConfig> columnConfigs = formReleaseConfig.getListConfig().getColumnConfigs();
        for (ColumnConfig config : columnConfigs) {
            head.add(Collections.singletonList(config.getLabel()));
        }

        List<Map<Integer, Object>> exportList = new ArrayList<>();
        if (!dto.getIsTemplate()) {
            // 构建导出数据格式
            dto.setIsTrans(false);
//            Boolean isPage = formReleaseConfig.getListConfig().getIsPage();
            List<Entity> dataList = formExecuteService.list(dto);
//            if (isPage) {
//                dataList = formExecuteService.page(dto).getList();
//            } else {
//                dataList = formExecuteService.list(BeanUtil.toBean(dto, FormExecuteListDto.class));
//            }
            // 时间日期格式转换

            for (Entity entity : dataList) {
                Map<Integer, Object> exportData = new HashMap<>(entity.size());
                for (int i = 0; i < columnConfigs.size(); i++) {
                    ColumnConfig config = columnConfigs.get(i);
                    Object value = entity.get(config.getColumnName());
                    String componentType = config.getComponentType();
                    // 日期时间字段格式化
                    if ((StrUtil.equalsIgnoreCase(ComponentTypeConstant.DATE, componentType)
                            || StrUtil.equalsIgnoreCase(ComponentTypeConstant.DATE_RANGE, componentType))) {
                        String format = LocalDateTimeUtil.convertFontFormat(config.getFormat());
                        LocalDateTime dateTime = null;
                        if (value instanceof Date) {
                            dateTime = ((Date) value).toLocalDate().atStartOfDay();
                        } else if (value instanceof Timestamp) {
                            dateTime = ((Timestamp)value).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                        }
                        value = LocalDateTimeUtil.format(dateTime, format);
                    } else if ((StrUtil.equalsIgnoreCase(ComponentTypeConstant.TIME, componentType)
                            || StrUtil.equalsIgnoreCase(ComponentTypeConstant.TIME_RANGE, componentType))) {
                        LocalTime time = null;
                        if (value instanceof Time) {
                            time = ((Time) value).toLocalTime();
                        } else if (value instanceof INTERVALDS) {
                            time = LocalDateTimeUtil.convertIntervalToLocalTime((INTERVALDS)value);
                        }
                        value = LocalDateTimeUtil.format(time);
                    }
                    exportData.put(i, value);
                }
                exportList.add(exportData);
            }
        }
        ByteArrayOutputStream bot = new ByteArrayOutputStream();
        EasyExcel.write(bot).head(head).automaticMergeHead(false)
                .registerWriteHandler(new FormContentStyleStrategy())
                .excelType(ExcelTypeEnum.XLSX).sheet().doWrite(exportList);
        return R.fileStream(bot.toByteArray(), formReleaseConfig.getMenuConfig().getName() + ExcelTypeEnum.XLSX.getValue());
    }

    @PostMapping("/import")
    @ApiOperation(value = "导入")
    public R importData(@RequestParam String releaseId, @RequestParam MultipartFile file) throws IOException {
        FormRelease formRelease = formReleaseService.getById(releaseId);
        FormReleaseConfig formReleaseConfig = JSONUtil.toBean(formRelease.getConfigJson(), FormReleaseConfig.class);
        // 配置excel第一行字段名
        List<List<String>> head = new ArrayList<>();
        List<ColumnConfig> columnConfigs = formReleaseConfig.getListConfig().getColumnConfigs();
        for (ColumnConfig config : columnConfigs) {
            head.add(Collections.singletonList(config.getLabel()));
        }
        List<Map<Integer, Object>> dataList = EasyExcel.read(file.getInputStream()).head(head).sheet().doReadSync();
        // 构建保存数据
        List<Map<String, Object>> savedDataList = new ArrayList<>(dataList.size());
        for (Map<Integer, Object> data : dataList) {
            Map<String, Object> saveData = new HashMap<>(data.size());
            for (int i = 0; i < columnConfigs.size(); i++) {
                ColumnConfig columnConfig = columnConfigs.get(i);
                saveData.put(columnConfig.getColumnName(), data.get(i));
            }
            savedDataList.add(saveData);
        }
        formExecuteService.saveMainBatch(formRelease.getFormId(), savedDataList);
        return R.ok();
    }

    @PutMapping("/data-auth")
    @ApiOperation(value = "批量设置权限所属人")
    public R setDataAUth(@RequestBody BatchSetDataAuthDto dto, @RequestParam String releaseId){
        FormRelease formRelease = formReleaseService.getById(releaseId);
        FormTemplate template = formTemplateService.getById(formRelease.getFormId());
        String formJson = template.getFormJson();
        //自定义表单配置
        FormDesignConfig formDesignConfig = JSONUtil.toBean(formJson, FormDesignConfig.class);
        //表关系配置
        List<TableConfig> tableConfigs = formDesignConfig.getTableConfigs();
        //主表
        Optional<TableConfig> mainTable = tableConfigs.stream().filter(TableConfig::getIsMain).findFirst();
        String tableName = mainTable.get().getTableName();
        // 修改的数据
        Entity entity = Entity.create(tableName).set(GlobalConstant.AUTH_USER_ID, dto.getUserIdList().get(0));
        Entity where = Entity.create(tableName).set(mainTable.get().getPkField(), dto.getDataIdList());
        try {
            Db.use(DatasourceUtil.getDataSource(formDesignConfig.getDatabaseId())).update(entity, where);
        } catch (SQLException e) {
            throw new MyException("批量设置权限所属人失败！");
        }
        return R.ok();
    }
}
